package com.starview.star.operators.string.runtime;

import java.util.Map.Entry;

import com.starview.platform.data.IArray;
import com.starview.platform.data.IConstructor;
import com.starview.platform.data.IFunction;
import com.starview.platform.data.IMap;
import com.starview.platform.data.IPattern;
import com.starview.platform.data.IRecord;
import com.starview.platform.data.IRelation;
import com.starview.platform.data.IScalar;
import com.starview.platform.data.IValue;
import com.starview.platform.data.IValueVisitor;
import com.starview.platform.data.type.DisplayType;
import com.starview.platform.data.type.TypeUtils;
import com.starview.star.compiler.util.PrettyPrintDisplay;
import com.starview.star.compiler.util.PrettyPrintable;

public class ValueDisplay implements IValueVisitor
{
  private final PrettyPrintDisplay disp;
  public static final String LIST = "list";
  public static final String HASH = "hash";
  public static final String EQUAL = "=";
  public static final String INDEXED = "indexed";
  public static final String RELATION = "relation";

  private ValueDisplay(PrettyPrintDisplay disp)
  {
    this.disp = disp;
  }

  public static String display(IValue term)
  {
    PrettyPrintDisplay disp = new PrettyPrintDisplay();
    ValueDisplay displayer = new ValueDisplay(disp);
    term.accept(displayer);
    return disp.toString();
  }

  public static void display(PrettyPrintDisplay disp, IValue term)
  {
    ValueDisplay displayer = new ValueDisplay(disp);
    term.accept(displayer);
  }

  @Override
  public void visitArray(IArray array)
  {
    disp.append("array of {");
    int mark = disp.markIndent(2);
    String sep = "";
    for (IValue el : array) {
      disp.append(sep);
      sep = ";\n";
      el.accept(this);
    }
    disp.popIndent(mark);
    disp.append("}");
  }

  @Override
  public void visitConstructor(IConstructor con)
  {
    String label = con.getLabel();

    if (!TypeUtils.isTupleLabel(label) && !TypeUtils.isAnonRecordLabel(label))
      disp.appendId(label);
    if (con.size() > 0 || TypeUtils.isTupleLabel(label)) {
      disp.append("(");
      String sep = "";
      for (int ix = 0; ix < con.size(); ix++) {
        disp.append(sep);
        sep = ", ";
        con.getCell(ix).accept(this);
      }
      disp.append(")");
    }
  }

  @Override
  public void visitFunction(IFunction fn)
  {
    disp.append("<<function: ");
    DisplayType.display(disp, fn.getType());
    disp.append(">>");
  }

  @Override
  public void visitPattern(IPattern ptn)
  {
    disp.append("<<pattern: ");
    DisplayType.display(disp, ptn.getType());
    disp.append(">>");
  }

  @Override
  public void visitMap(IMap map)
  {
    disp.append(HASH);
    disp.append("{");
    int mark = disp.markIndent(2);
    String sep = "";
    for (Entry<IValue, IValue> entry : map) {
      disp.append(sep);
      sep = ";\n";
      entry.getKey().accept(this);
      disp.append("->");
      entry.getValue().accept(this);
    }
    disp.popIndent(mark);
    disp.append("}");
  }

  @Override
  public void visitRecord(IRecord record)
  {
    if (!TypeUtils.isAnonRecordLabel(record.getLabel()))
      disp.appendId(record.getLabel());
    disp.append("{");
    int mark = disp.markIndent(2);
    String sep = "";
    String fields[] = record.getMembers();
    for (int ix = 0; ix < record.size(); ix++) {
      disp.append(sep);
      sep = ";\n";
      disp.appendId(fields[ix]);
      disp.appendWord(EQUAL);
      disp.append(" ");
      IValue value = record.getCell(ix);
      if (value != null)
        value.accept(this);
      else
        disp.append("(null)");
    }
    disp.popIndent(mark);
    disp.append("}");
  }

  @Override
  public void visitRelation(IRelation relation)
  {
    int mark = disp.markIndent(2);
    disp.appendWord(RELATION);
    disp.appendWord("{");
    String sep = "\n";
    for (IValue tuple : relation) {
      disp.append(sep);
      sep = ";\n";
      tuple.accept(this);
    }
    disp.popIndent(mark);
    disp.append("\n");
    disp.appendWord("}");
  }

  @Override
  public void visitScalar(IScalar<?> scalar)
  {
    if (scalar instanceof PrettyPrintable)
      ((PrettyPrintable) scalar).prettyPrint(disp);
    else
      disp.append(scalar.getValue().toString());
  }
}
